//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
namespace Microsoft.Research.eResearch.TridentExecutionService
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Linq;
    using System.ServiceProcess;
    using System.Text;
    using System.Threading;
    using System.IO;
    using Microsoft.Research.eResearch.WFServiceLibrary;
    using System.Configuration;
    using Microsoft.Practices.EnterpriseLibrary.Logging;
    using Microsoft.Research.eResearch.Common.Logging;

    /// <summary>
    /// This is the windows service which is installed.
    /// This would take care of executing the Job which are queued in teh registry.
    /// </summary>
    public partial class ExecutionService : ServiceBase
    {
        /// <summary>
        /// Scheduler Thread.
        /// </summary>
        private Thread scheduleThread;

        /// <summary>
        /// Scheduler Thread.
        /// </summary>
        private Thread scheduleUpdaterThread;

        /// <summary>
        /// Machine Name for configurin gthe Scheduler.
        /// </summary>
        private string serviceInstanceName;

        /// <summary>
        /// Executor path for executing the Workflow.
        /// </summary>
        private string wfAppPath;

        /// <summary>
        /// Executor path for executing the schedule.
        /// </summary>
        private string scheduleExecuterApp;

        private WFServiceScheduler scheduler = null;

        /// <summary>
        /// Constructor for the teh execution Service.
        /// </summary>
        /// <param name="serviceInstanceName">Machine Name where the service is installed.</param>
        /// <param name="wfAppPath">Workflow Executor Path.</param>
        public ExecutionService(
            string serviceInstanceName,
            string workflowAppPath,
            string scheduleExecuterApp)
        {
            if (string.IsNullOrEmpty(serviceInstanceName) ||
                string.IsNullOrEmpty(workflowAppPath))
            {
                throw new ArgumentException("Agruments are not in correct format.");
            }

            InitializeComponent();

            // Report Start, Stop, Pause, and Continue commands in the event log.
            this.AutoLog = true;

            // Handle the exception which was not caught by the Service.
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

            this.serviceInstanceName = serviceInstanceName;
            this.wfAppPath = workflowAppPath;
            this.scheduleExecuterApp = scheduleExecuterApp;
        }

        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            StringBuilder errorMsg = new StringBuilder();
            errorMsg.AppendLine("Unhandled Exception was found : ");

            Exception exp = e.ExceptionObject as Exception;
            if (null != exp)
            {
                errorMsg.Append("Exception : ");
                errorMsg.Append(exp.Message);
            }

            Log("Error", errorMsg.ToString());
        }

        /// <summary>
        /// Overrided method for OnStart Event.
        /// </summary>
        /// <param name="args">Arguments for starting.</param>
        protected override void OnStart(string[] args)
        {
            Log("Info", "Attempting to start Workflow execution service...");

            Environment.CurrentDirectory = System.AppDomain.CurrentDomain.BaseDirectory;

            InitWorkflowScheduler();

            InitTaskCreator();

            Log("Info", "Workflow execution service started successfully.");
        }

        /// <summary>
        /// Initialize windows task creator.
        /// </summary>
        private void InitTaskCreator()
        {
            Log("Info", "Attempting to start windows task creator...");

            try
            {
                // Create WindowsTaskCreator thread
                WindowsTaskCreator taskCreater = new WindowsTaskCreator();
                string SchedulerPollTime = ConfigurationManager.AppSettings["SchedulerPollTimeInMilliseconds"];
                taskCreater.SchedulerEXEPath =
                    Path.Combine(Environment.CurrentDirectory, this.scheduleExecuterApp);
                taskCreater.HostName = this.serviceInstanceName; ;
                if (!string.IsNullOrEmpty(SchedulerPollTime))
                    taskCreater.SchedulerPollTime = Int32.Parse(SchedulerPollTime);

                // Event Handler for capturing the Terminate Event.
                taskCreater.OnTerminateScheduler +=
                    new WindowsTaskCreator.TerminateSchedulerEvent(this.Scheduler_OnTerminateScheduler);
                taskCreater.LogMessageReceived += new EventHandler<LogEventArgs>(this.OnLogMessageReceived);

                // Start New thread for updating scheduler information.
                this.scheduleUpdaterThread = new Thread(taskCreater.RunScheduleCreationThread);
                this.scheduleUpdaterThread.IsBackground = true;
                this.scheduleUpdaterThread.Start();
            }
            catch (Exception ex)
            {
                Log("Error", ex.ToString());
            }

            Log("Info", "Windows task creator started successfully.");
        }

        /// <summary>
        /// Initialize workflow scheduler.
        /// </summary>
        private void InitWorkflowScheduler()
        {
            Log("Info", "Attempting to start workflow scheduler service...");

            try
            {
                // Configure the Scheduler with the details.
                scheduler = new WFServiceScheduler();
                scheduler.HostName = this.serviceInstanceName;
                scheduler.WFProcessName = this.wfAppPath;

                string attempts = ConfigurationManager.AppSettings["SQLReconnectAttempts"];
                if (!string.IsNullOrEmpty(attempts))
                {
                    scheduler.configurableReconnectAttempts = Int32.Parse(attempts);
                }

                // Event Handler for capturing the Terminate Event.
                scheduler.OnTerminateScheduler += new WFServiceScheduler.TerminateSchedulerEvent(this.Scheduler_OnTerminateScheduler);
                scheduler.LogMessageReceived += new EventHandler<LogEventArgs>(this.OnLogMessageReceived);

                // Start New thread for Running the pending workflows.
                this.scheduleThread = new Thread(scheduler.RunWorkflows);
                this.scheduleThread.IsBackground = true;
                this.scheduleThread.Start();
            }
            catch (Exception ex)
            {
                Log("Error", ex.ToString());
            }

            Log("Info", "Workflow scheduler service started successfully.");
        }

        /// <summary>
        /// Overrided method for OnStop Event.
        /// </summary>
        protected override void OnStop()
        {
            Log("Info", "Workflow scheduler service stopped successfully.");
        }

        /// <summary>
        /// Method Handlers for the Terminate the execution of the workflow.
        /// </summary>
        /// <param name="message">Cause for termination.</param>
        private void Scheduler_OnTerminateScheduler(string message)
        {
            Log("Error", message + "\r\nExecution service will be closed, please resolve the issue & restart it again.");
            this.Stop();
        }

        /// <summary>
        /// this function is used to log the message.
        /// </summary>
        /// <param name="level">Level.</param>
        /// <param name="message">Message to be logged.</param>
        private void Log(string level, string message)
        {
            Logger.Write(message, "FileLoggerExecutionService");
        }

        /// <summary>
        /// Log message received event handler.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Log event agrs.</param>
        void OnLogMessageReceived(object sender, LogEventArgs e)
        {
            this.Log(e.Level, e.Message);
        }
    }
}
